package ac.svn.accesscontrol.manager;

import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;

import ac.notification.event.INotificationListener;
import ac.notification.event.NotificationEvent;
import ac.notification.manager.NotificationRegistrar;
import ac.notification.service.INotificationIdetifier;
import ac.svn.accesscontrol.context.Context;
import ac.svn.accesscontrol.context.ContextHandler;
import ac.svn.accesscontrol.context.IContextConstants;
import ac.svn.accesscontrol.controller.AccessRightEditingController;
import ac.svn.accesscontrol.store.IContextName;
import ac.svn.accesscontrol.store.IResources;
import ac.svn.accesscontrol.store.ISVNIdentifier;
import ac.svn.accesscontrol.store.ISVNItem;
import ac.svn.accesscontrol.store.SVNLocation;
import ac.svn.accesscontrol.ui.IUIConstants;
import ac.svn.accesscontrol.ui.UIComponentLibrary;
import ac.svn.accesscontrol.ui.helper.LocationButtonActionListener;
import ac.svn.accesscontrol.ui.helper.UniqueLocationFinder;
import ac.svn.accesscontrol.ui.window.LocationManagerWindow;
import ac.svn.accesscontrol.utils.Utility;

public class LocationInformationManager implements 
INotificationListener,
ISVNIdentifier,
IContextName,
INotificationIdetifier,
IContextConstants,
IUIConstants
{
	public interface ExportedComponents
	{
		public static final String C_ERROR_LABEL = "C_LOC_MANAGER_ERROR_LABEL";
	}    

	private AccessRightEditingController editingController;
	private Context editingContext;
	private LocationManagerWindow managerWindow;
	private UniqueLocationFinder uniquePathFinder, uniqueNameFinder;
	private JList targetList;

	//private 
	public LocationInformationManager()
	{
		managerWindow = new LocationManagerWindow();
		
		managerWindow.setIconImage(Utility.getImage(IResources.LOC_ICN_URL));
		
		UIComponentLibrary.addComponent(ExportedComponents.C_ERROR_LABEL, managerWindow.getStatusLabel());
		targetList = (JList) UIComponentLibrary.getComponent(IUIConstants.C_LOCATION_LIST);

		uniqueNameFinder = new UniqueLocationFinder(managerWindow.getLocationNameField());
		uniquePathFinder = new UniqueLocationFinder(managerWindow.getLocationPathField());

		uniqueNameFinder.setLocAttribute(UniqueLocationFinder.LOC_ATTRIBUTE.NAME);
		uniquePathFinder.setLocAttribute(UniqueLocationFinder.LOC_ATTRIBUTE.PATH);

		editingController = new AccessRightEditingController();

		registerListeners();

		//Create a context for editing purposes.
		editingContext = ContextHandler.getContextByName(CONTEXT_LOCATION_MGR);
		if(null == editingContext)
		{
			ContextHandler.createContext(CONTEXT_LOCATION_MGR);
			editingContext = ContextHandler.getContextByName(CONTEXT_LOCATION_MGR);
		}

		NotificationRegistrar.registerListener(this);
	}

	private void registerListeners() 
	{
		ActionListener closeActionListener = new ActionListener() 
		{
			@Override
			public void actionPerformed(ActionEvent arg0) 
			{
				managerWindow.setVisible(false);
			}
		};
		managerWindow.getCloseMI().addActionListener(closeActionListener);
		managerWindow.getCloseButton().addActionListener(closeActionListener);

		managerWindow.getLocationNameField().addKeyListener(uniqueNameFinder);
		managerWindow.getLocationNameField().addFocusListener(uniqueNameFinder);

		managerWindow.getLocationPathField().addKeyListener(uniquePathFinder);
		managerWindow.getLocationPathField().addFocusListener(uniquePathFinder);

		managerWindow.getDoneButton().addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) 
			{
				if(false ==verifyAdditionValid())
				{
					JOptionPane.showMessageDialog(managerWindow,"Operation cannot be completed!");
				}
				else
				{
					finalizeOperation();
				}
			}	   
		});

		//Add Action Listener for Add location button.
		LocationButtonActionListener actionListener = new LocationButtonActionListener(managerWindow.getAddLocationButton(),
				CMD_ADD_LOCATION,
				null);
		actionListener.setDataSourceView(targetList);
		managerWindow.getAddLocationButton().addActionListener(actionListener);
		managerWindow.getAddLocationMI().addActionListener(actionListener);

		//Add Action listener to Delete Location button.
		actionListener = new LocationButtonActionListener(managerWindow.getAddLocationButton(),
				CMD_DELETE_LOCATION,
				P_LOCATION);
		actionListener.setDataSourceView(targetList);
		managerWindow.getDeleteLocationButton().addActionListener(actionListener);
		managerWindow.getDeleteLocationMI().addActionListener(actionListener);

		//Add Action  Listener to Edit location button.
		actionListener = new LocationButtonActionListener(managerWindow.getAddLocationButton(),
				CMD_EDIT_LOCATION,
				P_LOCATION);
		actionListener.setDataSourceView(targetList);
		managerWindow.getEditLocationButton().addActionListener(actionListener);
		managerWindow.getEditLocationMI().addActionListener(actionListener);

		//Show help on Help menu item press.
		managerWindow.getHelpMI().addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				String message = "<HTML> Locations Manager Help Topics:<br>" +
						"1. To add a location, Press \"Add\" button and \"Add Location\" from Action menu.<br>" +
						"2. Select a location from list and press on \"Edit\" for changing location's properties.<br>" +
						"3. Select a location from list and press on \"Delete\" to delete a location from access file and associated access rights.<br><br>" +
						"\"Done\" to save changes if any.<br>" +
						"\"No operation in current context!\" may appear on \"Done\" button when operations are not performed in mentioned way!";
				JOptionPane.showMessageDialog(managerWindow,message,"Help",JOptionPane.INFORMATION_MESSAGE);
			}
		});

		managerWindow.getAccessRightsBtn().addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				modifyAccessRights();
			}
		});
	}

	protected void modifyAccessRights() 
	{
		int operationType = ((Integer)editingContext.get(KEY_OPERATION_TYPE)).intValue();
		if(VALUE_OP_TYPE_ADDITION == operationType)
		{
			//Name is left empty
			boolean isNameEmpty =  managerWindow.getLocationNameField().getText().trim().isEmpty();

			//Path is left empty
			boolean isPathEmpty =  managerWindow.getLocationPathField().getText().trim().isEmpty();

			//Check if path or Name is left empty before proceeding.
			if((true ==isNameEmpty) || (true == isPathEmpty))
			{
				String message = "Wait, before editing access rights for this location please mention valid name and path!";
				JOptionPane.showMessageDialog(managerWindow,message,"Information Required",JOptionPane.WARNING_MESSAGE);
				return;
			}
			else
			{
				//Get temporary object from context.
				SVNLocation svnLocation = (SVNLocation) editingContext.get(KEY_OPERATION_PARAM);

				//Check if at all it is available.
				if(null != svnLocation)
				{
					svnLocation.setLocationName(managerWindow.getLocationNameField().getText());
					svnLocation.setLocationPath(managerWindow.getLocationPathField().getText());
				}
			}
		}		
		Utility.sendNotification(CMD_MODIFY_ACCESS_RIGHTS, editingContext);
	}

	@Override
	public Set<String> declaredSubscriptionSet() 
	{
		Set<String> subscriptionSet = new HashSet<String>();
		subscriptionSet.add(CMD_SHOW_LOC_INFO_MGR);
		subscriptionSet.add(CMD_ADD_LOCATION);
		subscriptionSet.add(CMD_DELETE_LOCATION);
		subscriptionSet.add(CMD_EDIT_LOCATION);
		subscriptionSet.add(CMD_EDIT_ACCESS);
		subscriptionSet.add(CMD_SELECT_LOCATION);
		subscriptionSet.add(N_LOCATION_EDITOR_VISIBILTY_CHANGE);
		subscriptionSet.add(N_LOCATION_SELECTION);
		subscriptionSet.add(N_LOCATION_REMOVED);
		return subscriptionSet;
	}

	@Override
	public void receiveEvent(NotificationEvent ne) 
	{
		try
		{
			if(ne.getPropertyIdentifier().contains(CMD_SHOW_LOC_INFO_MGR))
			{
				restoreUIState();
				setFieldData("", false, "", false,false);
				showManagerWindow();
			}
			else if(ne.getPropertyIdentifier().contains(CMD_ADD_LOCATION))
			{
				restoreUIState();
				addLocation();
			}
			else if(ne.getPropertyIdentifier().contains(CMD_EDIT_LOCATION))
			{
				//Get Location which is to edit.
				ISVNItem location = (ISVNItem) ne.getParameter(P_LOCATION);

				//Check whether location has been provided correctly
				if(null != location)
				{
					//Clear changes in UI made in previous operation.
					restoreUIState();

					//Perform Editing 
					editLocation(location);
				}
			}
			else if(ne.getPropertyIdentifier().contains(CMD_DELETE_LOCATION))
			{
				//Get Location which is to delete.
				ISVNItem location = (ISVNItem) ne.getParameter(P_LOCATION);
				Boolean auto			= (Boolean) ne.getParameter(P_AUTOMATIC);

				//Check whether location has been provided correctly
				if(null != location)
				{
					//Clear changes in UI made in previous operation.
					restoreUIState();

					//Perform Deletion
					deleteLocation(location,auto);	
				}

			}
			else if(ne.getPropertyIdentifier().contains(N_LOCATION_EDITOR_VISIBILTY_CHANGE))
			{
				Boolean visible = (Boolean) ne.getPropertyValue();
				if(Boolean.TRUE.equals(visible))
				{
					targetList.clearSelection();
					Component pane = (Component) UIComponentLibrary.getComponent(IUIConstants.C_LOCATION_LIST_VIEW_HOLDER);
					managerWindow.addLocationListView(pane);

				}
			}
			else if(ne.getPropertyIdentifier().contains(N_LOCATION_SELECTION))
			{
				String locationPath = ne.getPropertyValue().toString();
				managerWindow.getStatusLabel().setText(locationPath);
			}
			else if(ne.getPropertyIdentifier().contains(CMD_SELECT_LOCATION))
			{
				ISVNItem location = (ISVNItem) ne.getPropertyValue();
				showLocation(location);
			}
			else if(ne.getPropertyIdentifier().contains(CMD_EDIT_ACCESS))
			{
				ISVNItem location = (ISVNItem) ne.getParameter(P_LOCATION);
				SVNLocation svnLocation = SVNInfoManager.getInfoManager().getLocation(location.getId());
				if(null != svnLocation)
				{
					editingContext.put(KEY_OPERATION_TYPE,VALUE_OP_TYPE_EDITING);
					editingContext.put(KEY_OPERATION_PARAM, svnLocation);
					modifyAccessRights();
				}
			}
			else if(ne.getPropertyIdentifier().contains(N_LOCATION_REMOVED))
			{
				showDeleteNotification();
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
			System.err.println(String.format("Error occurred while processing event %s. Description: %s",ne.getPropertyIdentifier(),e.getMessage()));
		}

	}

	private void showDeleteNotification()
	{
		SwingUtilities.invokeLater(new Runnable() 
		{
			@Override
			public void run() 
			{
				String message = "Location has been removed successfully!";
				managerWindow.getStatusLabel().setIcon(Utility.getImageIcon(IResources.LOC_ICN_DONE));
				managerWindow.getStatusLabel().setText(message);
				JOptionPane.showMessageDialog(managerWindow, message,"Operation Completed",JOptionPane.INFORMATION_MESSAGE);
			}
		});		
	}

	private void deleteLocation(ISVNItem location,final Boolean automatic)
	{
		editingContext.put(KEY_OPERATION_TYPE,VALUE_OP_TYPE_UNDEFINED);
		if((null != location) &&(location instanceof SVNLocation))
		{
			if(null != managerWindow)
			{
				managerWindow.getStatusLabel().showInformationText("Performing deletion operation!");
				SVNLocation svnLocation = (SVNLocation) location;
				managerWindow.getStatusLabel().showInformationText(String.format("<HTML> <b>Deleting</b> location [%s], press <b>Done</b> when ready!</HTML>",svnLocation.getLocationPath()));

				Utility.showSystemNotification(String.format(String.format("<HTML> <b>Deleting</b> location [%s], press <b>Done</b> when ready!</HTML>",svnLocation.getLocationPath())));
				editingContext.put(KEY_OPERATION_TYPE,VALUE_OP_TYPE_DELETION);
				editingContext.put(KEY_OPERATION_PARAM, svnLocation);

				setFieldData(svnLocation.getLocationPath(), false, svnLocation.getLocationName(), false,false);

				//showManagerWindow();

				//Let UI update information on screen before deleting user.
				SwingUtilities.invokeLater(new Runnable() 
				{
					@Override
					public void run() 
					{
						if((null != automatic) && (Boolean.TRUE.equals(automatic)))
						{
							finalizeOperation();
						}						
					}
				});


			}
		}
		else
		{
			System.out.println(String.format("LocationInformationManager.deleteLocation(): NULL or Unknown data type %s encountered for deletion!",(null != location)?location.getClass().getSimpleName():"NULL"));
		}
	}

	private void showLocation(ISVNItem location)
	{
		editingContext.put(KEY_OPERATION_TYPE,VALUE_OP_TYPE_UNDEFINED);

		if((null != location) &&(location instanceof SVNLocation))
		{
			if(null != managerWindow)
			{
				SVNLocation svnLocation = (SVNLocation) location;

				managerWindow.getStatusLabel().showInformationText(String.format("<HTML><b>%s</b>[%s].</HTML>",svnLocation.getLocationName(),svnLocation.getLocationPath()));

				setFieldData(svnLocation.getLocationPath(), false, svnLocation.getLocationName(), false,false);

				uniqueNameFinder.resetBorder();
				uniquePathFinder.resetBorder();

				showManagerWindow();
			}
		}
		else
		{
			System.out.println(String.format("LocationInformationManager.editLocation(): NULL or Unknown data type %s encountered for editing!",(null != location)?location.getClass().getSimpleName():"NULL"));
		}
	}  


	private void editLocation(ISVNItem location)
	{
		editingContext.put(KEY_OPERATION_TYPE,VALUE_OP_TYPE_UNDEFINED);

		if((null != location) &&(location instanceof SVNLocation))
		{
			if(null != managerWindow)
			{
				SVNLocation svnLocation = (SVNLocation) location;

				managerWindow.getStatusLabel().showInformationText(String.format("<HTML> <b>Editing</b> location [%s], press <b>Done</b> when finished.</HTML>",svnLocation.getLocationPath()));
				Utility.showSystemNotification(String.format("<HTML> <b>Editing</b> location [%s], press <b>Done</b> when finished.</HTML>",svnLocation.getLocationPath()));
				editingContext.put(KEY_OPERATION_TYPE,VALUE_OP_TYPE_EDITING);
				editingContext.put(KEY_OPERATION_PARAM, svnLocation);

				setFieldData(svnLocation.getLocationPath(), false , svnLocation.getLocationName(), true,true);

				uniqueNameFinder.setExistingValue(svnLocation.getLocationName());
				uniquePathFinder.setExistingValue(svnLocation.getLocationPath());

				showManagerWindow();
			}
		}
		else
		{
			System.out.println(String.format("LocationInformationManager.editLocation(): NULL or Unknown data type %s encountered for editing!",(null != location)?location.getClass().getSimpleName():"NULL"));
		}
	}    
	private void addLocation()
	{
		editingContext.put(KEY_OPERATION_TYPE,VALUE_OP_TYPE_UNDEFINED);

		if(null != managerWindow)
		{
			setFieldData("", true,"",true,true);

			SVNLocation tempLocation = new SVNLocation();

			editingContext.put(KEY_OPERATION_TYPE,VALUE_OP_TYPE_ADDITION);
			editingContext.put(KEY_OPERATION_PARAM,tempLocation);

			managerWindow.getStatusLabel().showInformationText("<HTML><b> Adding</b> Location, press <b>Done</b> when ready!");
			Utility.showSystemNotification("<HTML><b> Adding</b> Location, press <b>Done</b> when ready!");
			uniqueNameFinder.setExistingValue(null);
			uniquePathFinder.setExistingValue(null);

			managerWindow.getLocationNameField().requestFocusInWindow();

			showManagerWindow();
		}
	}

	private void showManagerWindow()
	{
		if(false == managerWindow.isVisible())
		{
			EventQueue.invokeLater(new Runnable() 
			{
				@Override
				public void run() 
				{
					managerWindow.setExtendedState(JFrame.MAXIMIZED_BOTH);
					managerWindow.setVisible(true);
					managerWindow.toFront();				
					managerWindow.repaint();					
				}
			});

		}

	}

	private void restoreUIState()
	{
		//Get Default border for any text field.
		Border defaultTextFieldBorder = (new JTextField()).getBorder();

		//Restore borders of the field.
		managerWindow.getLocationNameField().setBorder(defaultTextFieldBorder);
		managerWindow.getLocationPathField().setBorder(defaultTextFieldBorder);



	}

	private boolean verifyAdditionValid()
	{
		boolean operationValid = true; 
		Integer operationType = (Integer)editingContext.get(KEY_OPERATION_TYPE);

		if(null == operationType)
		{
			return false;
		}

		switch (operationType ) {
		case VALUE_OP_TYPE_UNDEFINED:
		{
			JOptionPane.showMessageDialog(managerWindow,"<HTML>No operation in current context!<br>Go to Help for more information!","No Operation!",JOptionPane.WARNING_MESSAGE);
		}
		break;

		case VALUE_OP_TYPE_ADDITION:
		case VALUE_OP_TYPE_EDITING:
		{
			operationValid &= uniqueNameFinder.isCurrentOpValid();
			operationValid &= uniquePathFinder.isCurrentOpValid();
		}	
		break;
		default:
			break;
		}

		return operationValid;
	}


	private void finalizeOperation() 
	{
		int operationType = ((Integer)editingContext.get(KEY_OPERATION_TYPE)).intValue();
		String locationPath = managerWindow.getLocationPathField().getText();
		String locationName = managerWindow.getLocationNameField().getText();

		String notificationId = N_LOCATION_ADDED;
		
		String opStatusMessage = "Tell me what I need to do next!";

		//Get latest location from context.
		SVNLocation location = (SVNLocation) editingContext.get(KEY_OPERATION_PARAM);
		if(null != location)
		{
			//Update location details.
			location.setLocationName(locationName);
			location.setLocationPath(locationPath);
		}


		switch (operationType ) 
		{
		case VALUE_OP_TYPE_ADDITION:
		{
			//Update information in SVN INformation Manager.
			SVNInfoManager.getInfoManager().updateLocation(location);

			//Create a new message for operation.
			opStatusMessage = "Location has been added successfully!";

			
			notificationId = N_LOCATION_ADDED;
			
			//Update new changes on dialog.
			Utility.sendNotification(ISVNIdentifier.CMD_SHOW_LOC_INFO_MGR, location);
		}
		break;
		case VALUE_OP_TYPE_EDITING:
		{
			if(null != location)
			{
				//Update information in SVN INformation Manager.
				SVNInfoManager.getInfoManager().updateLocation(location);

				//Create a new message for operation.
				opStatusMessage = "Location has been updated successfully!";

				notificationId = N_LOCATION_MODIFIED;
				
				//Update new changes on dialog.
				Utility.sendNotification(ISVNIdentifier.CMD_SHOW_LOC_INFO_MGR, location);
			}
		}
		break;
		case VALUE_OP_TYPE_DELETION:
		{
			//Format warning message.
			String message = "<HTML><p align=\"LEFT\">Are you sure you want to delete this location.<br>" +
					"All modification rights associated with the location will no longer be valid!</p>Continue?";

			//Display warning dialog.
			int selectedOption = JOptionPane.showConfirmDialog(managerWindow, message, "Delete Location!",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE);

			//If user confirms deletion operation.
			if(JOptionPane.YES_OPTION == selectedOption)
			{
				if(null != location)
				{
					//Message to be displayed.
					opStatusMessage = "Deleting location..";

					Utility.sendNotification(CMD_DELETE_ENTITY, location);

					//Refresh dialog.
					//Utility.sendNotification(ISVNIdentifier.CMD_SHOW_LOC_INFO_MGR, location);
				}
			}
			else		//In case user cancels deletion operation.
			{

				//Format message to be displayed.
				opStatusMessage = " You've cancelled the last operation, " + opStatusMessage;
				opStatusMessage = Utility.toHTML(opStatusMessage, true,false,false);
			}
		}
		default:
			break;
		}

		editingContext.clear();

		//Reset context for next operation.
		editingContext.put(KEY_OPERATION_TYPE, VALUE_OP_TYPE_UNDEFINED);

		//Display status message.
		managerWindow.getStatusLabel().setTextWithIcon(opStatusMessage, IResources.LOC_ICN_DONE);

		//Generate system notification.
		Utility.showSystemNotification(opStatusMessage);

		//Notify all components showing list to update with new changes.
		Utility.sendNotification(N_UPDATE_LISTINGS,C_LIST_TYPE_LOCATION);
		
		Utility.sendNotification(notificationId);
	}


	private void setFieldData(String path,boolean pathEnabled,String name,boolean nameEnabled,boolean accessBtnState)
	{
		managerWindow.getLocationNameField().setText(name);
		managerWindow.getLocationPathField().setText(path);

		managerWindow.getLocationNameField().setEditable(nameEnabled);
		managerWindow.getLocationPathField().setEditable(pathEnabled);

		managerWindow.getAccessRightsBtn().setEnabled(accessBtnState);
	}

	/**
	 * @return the editingController
	 */
	public AccessRightEditingController getEditingController() {
		return editingController;
	}

	/**
	 * @param editingController the editingController to set
	 */
	public void setEditingController(AccessRightEditingController editingController) {
		this.editingController = editingController;
	}



}
